Skip to content

Druid 1.2.21 踩坑实录:ORA-18730 Socket read timed out 超时陷阱根治方案

🕒 Published at:

Druid 1.2.21 踩坑实录:ORA-18730 Socket read timed out 超时陷阱根治方案 ​

前言 ​

近期项目定时任务执行一条复杂Oracle超大UPDATE语句,持续稳定复现异常: java.sql.SQLRecoverableException: ORA-18730: IO 中断错误。: Socket read timed out 任务执行耗时稳定在10s左右触发超时,无论怎么调整JDBC URL参数、升级ojdbc驱动、开启TCP保活、关闭NIO通信,问题始终无法根除。 层层排查后定位根因:Druid 1.2.12 ~ 1.2.21 存在致命超时默认值BUG,本文完整记录排障全过程,避免大家重复踩坑。

一、故障现象 ​

  1. 异常堆栈核心信息
java.sql.SQLRecoverableException: ORA-18730: IO 中断错误。: Socket read timed out
Caused by: java.net.SocketTimeoutException: Read timed out
  1. 关键特征
  • Oracle数据库部署在本地 127.0.0.1,不存在防火墙、安全组切断TCP连接;
  • JDBC URL已配置 oracle.jdbc.ReadTimeout=600000(10分钟),理论远大于故障触发的10s;
  • Oracle会话无锁等待,等待事件为 SQL*Net message to client,数据库执行完成后无法回传数据包;
  • 更换ojdbc8驱动(12.2.0.1 → 21.9.0.0)、开启oracle.jdbc.disableNIO=true、TCP保活,问题依旧复现;
  • 超时触发时间固定趋近10秒,高度可疑硬编码阈值。

二、层层排除误区 ​

误区1:怀疑SQL性能问题 ​

约束场景:本次先不优化SQL,优先解决通信异常。 排查Oracle会话视图,不存在行锁、表锁阻塞,SQL能够正常运算完成,故障发生在数据库向应用返回报文阶段。

误区2:怀疑ojdbc驱动NIO通信BUG ​

升级ojdbc8至21.9.0.0,关闭NIO、调低预取行数、开启TCP保活,只能缓解,无法彻底杜绝。 本质:驱动层参数优先级低于连接池注入参数,底层被Druid强制覆盖。

误区3:误认为Druid socket-timeout 参数对Oracle无效 ​

网上大量旧文章说明:Druid的socket-timeout仅适配MySQL,Oracle Thin驱动不识别。 该结论在Druid 1.2.12~1.2.21版本完全失效!

三、根因定位:Druid官方已知BUG ​

Druid 1.2.12 ~ 1.2.21

版本改动逻辑: 从1.2.12开始,如果开发者没有主动配置socket-timeout、connect-timeout,Druid内部会硬编码默认值 10000ms(10秒),并且在创建物理数据库连接时,强制将该参数注入JDBC连接属性。 参数优先级:Druid连接池属性 > JDBC URL内配置的oracle.jdbc.ReadTimeout。

哪怕URL配置10分钟超时,Druid依然强制给Oracle连接设置10s socket超时。 当慢SQL执行超过10秒,直接触发 Socket read timed out。

image.png

官方修复记录:#5735

Druid 1.2.22 版本移除这套硬编码默认逻辑,未配置超时参数时,不再强制赋值10秒,恢复原有行为。

四、解决方案 ​

本次直接升级到稳定版 1.2.23

xml
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.23</version>
</dependency>

五、配套标准化数据源配置(Oracle生产推荐模板) ​

properties
spring.datasource.dynamic.datasource.master.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.dynamic.datasource.master.url=jdbc:oracle:thin:@//127.0.0.1:1521/ORCL?remarksReporting=true&currentSchema=HIMMS&oracle.jdbc.ReadTimeout=600000&oracle.net.TCP_KEEPALIVE=true&oracle.jdbc.disableNIO=true&oracle.jdbc.defaultRowPrefetch=100&oracle.net.useBreakout=false
spring.datasource.dynamic.datasource.master.username=himms
spring.datasource.dynamic.datasource.master.password=himms

spring.datasource.dynamic.primary=master
spring.datasource.dynamic.strict=false

# Druid池配置
spring.datasource.dynamic.druid.initial-size=5
spring.datasource.dynamic.druid.max-active=20
spring.datasource.dynamic.druid.min-idle=5
spring.datasource.dynamic.druid.max-wait=6000000
spring.datasource.dynamic.druid.pool-prepared-statements=true
spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size=20
spring.datasource.dynamic.druid.validation-query-timeout=20000
spring.datasource.dynamic.druid.test-on-borrow=false
spring.datasource.dynamic.druid.test-on-return=false
spring.datasource.dynamic.druid.test-while-idle=true
spring.datasource.dynamic.druid.validation-query=SELECT 1 FROM DUAL
spring.datasource.dynamic.druid.time-between-eviction-runs-millis=5000
spring.datasource.dynamic.druid.min-evictable-idle-time-millis=180000
spring.datasource.dynamic.druid.filters=stat
spring.datasource.dynamic.druid.break-after-acquire-failure=false